The plan:
- Loading all data.
- Extracting statistics regarding PIDs (cases) and syscall types
(actions).
- Computation of basic aggregations and visualization of the
results.
The original source system audit logs are the property of
KnowledgePit.ai platform and can be downloaded from: https://knowledgepit.ai/fedcsis-2023-challenge/
The available data contains 20044 log files. Each of the files
corresponds to 10 minutes of syscall history. In total, 698 log files
(3.48%) correspond to attacks on the devices.
To enable the analysis of this data using process mining techniques,
I would suggest to consider the combination of a time window ID (
time_window_id ) and SYSCALL_pid
values as cases and combinations of
SYSCALL_syscall, SYSCALL_success, and
maybe PROCESS_uid values as actions.
I transform the data by selecting SYSCALL_timestamp,
SYSCALL_pid, SYSCALL_success,
PROCESS_uid, and SYSCALL_syscall
columns. I also merge all data tables into a single table for more
convenient processing. Finally, I divide the data into three parts:
- process_discovery part,
- model_training part,
- test_data part.
load(file = "process_data_v2.RData")
process_discovery_data[SYSCALL_success == "", SYSCALL_success := "NA"]
model_training_data[SYSCALL_success == "", SYSCALL_success := "NA"]
test_data[SYSCALL_success == "", SYSCALL_success := "NA"]
setkey(process_discovery_data, time_window_id, SYSCALL_timestamp)
setkey(model_training_data, time_window_id, SYSCALL_timestamp)
setkey(test_data, time_window_id, SYSCALL_timestamp)
eventlog_data <- eventlog(as.data.frame(process_discovery_data),
case_id = c("time_window_id", "SYSCALL_pid"),
timestamp = "SYSCALL_timestamp",
activity_id = c("PROCESS_uid", "SYSCALL_syscall", "SYSCALL_success"),
activity_instance_id = c("time_window_id", "SYSCALL_pid", "PROCESS_uid",
"SYSCALL_syscall", "SYSCALL_success", "SYSCALL_timestamp"),
resource_id = c("PROCESS_uid"),
lifecycle_id = "is_attack",
order = "alphabetical"
)
eventlog_data
# Log of 9500500 events consisting of:
159721 cases
2212620 instances of 77 activities
9 resources
Events occurred from 2023-09-01 until 2023-09-01 16:39:59
# Variables were mapped as follows:
Case identifier: time_window_id_SYSCALL_pid
Activity identifier: PROCESS_uid_SYSCALL_syscall_SYSCALL_success
Resource identifier: PROCESS_uid
Activity instance identifier: time_window_id_SYSCALL_pid_PROCESS_uid_SYSCALL_syscall_SYSCALL_success_SYSCALL_timestamp
Timestamp: SYSCALL_timestamp
Lifecycle transition: is_attack
trace_data <- traces(eventlog_data)
trace_data
frequent_traces <- filter_trace_frequency(eventlog_data, percentage = .8)
frequent_traces
# Log of 1697630 events consisting of:
127939 cases
851163 instances of 24 activities
3 resources
Events occurred from 2023-09-01 until 2023-09-01 16:39:59
# Variables were mapped as follows:
Case identifier: time_window_id_SYSCALL_pid
Activity identifier: PROCESS_uid_SYSCALL_syscall_SYSCALL_success
Resource identifier: PROCESS_uid
Activity instance identifier: time_window_id_SYSCALL_pid_PROCESS_uid_SYSCALL_syscall_SYSCALL_success_SYSCALL_timestamp
Timestamp: SYSCALL_timestamp
Lifecycle transition: is_attack
attack_traces <- filter_lifecycle_presence(eventlog_data, lifecycles = "TRUE", method = "all")
attack_traces
# Log of 462743 events consisting of:
5733 cases
89658 instances of 44 activities
5 resources
Events occurred from 2023-09-01 until 2023-09-01 16:39:59
# Variables were mapped as follows:
Case identifier: time_window_id_SYSCALL_pid
Activity identifier: PROCESS_uid_SYSCALL_syscall_SYSCALL_success
Resource identifier: PROCESS_uid
Activity instance identifier: time_window_id_SYSCALL_pid_PROCESS_uid_SYSCALL_syscall_SYSCALL_success_SYSCALL_timestamp
Timestamp: SYSCALL_timestamp
Lifecycle transition: is_attack
flow_plot <- process_map(frequent_traces, type = frequency("relative"))
flow_plot
frequent_attack_traces <- filter_trace_frequency(attack_traces, percentage = .8)
attacks_flow_plot <- process_map(frequent_attack_traces, type = frequency("relative"))
attacks_flow_plot
variant_viz <- trace_explorer(eventlog_data, coverage = 0.5)
variant_viz

eventlog_data_dt <- as.data.table(eventlog_data)
unique_action_ids <- eventlog_data_dt[, unique(PROCESS_uid_SYSCALL_syscall_SYSCALL_success)]
trace_dt <- eventlog_data_dt[,
.(trace = paste(PROCESS_uid_SYSCALL_syscall_SYSCALL_success,
collapse = ","),
trace_length = .N),
by = time_window_id_SYSCALL_pid]
dim(trace_dt)
[1] 159721 3
trace_dt[, uniqueN(trace)]
[1] 3351
trace_dt <- compact_traces(trace_dt, unique_action_ids,
show_progress = FALSE)
# trace_dt[, compacted_trace := copy(trace)]
#
# trace_dt[, compacted_trace := gsub("root_close_yes,root_openat_yes",
# "root_close_openat_yes",
# compacted_trace)]
# trace_dt[, compacted_trace := gsub("(,root_close_openat_yes){2,}",
# ",multi_root_close_openat_yes",
# compacted_trace)]
# trace_dt[, compacted_trace := gsub("^root_close_openat_yes,multi_root_close_openat_yes",
# "multi_root_close_openat_yes",
# compacted_trace)]
# trace_dt[, compacted_trace := gsub("^root_close_openat_yes,root_close_openat_yes",
# "multi_root_close_openat_yes",
# compacted_trace)]
# trace_dt[, uniqueN(compacted_trace)]
#
# for(action in unique_action_ids) {
# trace_dt[, compacted_trace := gsub(paste0("(,", action, "){2,}"),
# paste0(",multi_", action),
# compacted_trace)]
# trace_dt[, compacted_trace := gsub(paste0("^", action, ",multi_", action),
# paste0("multi_", action),
# compacted_trace)]
# trace_dt[, compacted_trace := gsub(paste0("^", action, ",", action),
# paste0("multi_", action),
# compacted_trace)]
# print(action)
# }
# trace_dt[, uniqueN(compacted_trace)]
# trace_dt[, compacted_trace_length := sapply(strsplit(compacted_trace, ","), length)]
normalized_levenshtein_dist <- function(x, y) {
ldist <- proxy::dist(list(x), list(y), method = "Levenshtein")
as.numeric(ldist)/(length(x) + length(y))
}
dist_matrix <- proxy::dist(strsplit(trace_dt[, unique(compacted_trace)], ","),
method = normalized_levenshtein_dist)
hclustering <- hclust(dist_matrix, method = "ward.D")
mds_embedds <- cmdscale(dist_matrix, k = 2)
plot(mds_embedds, col = cutree(hclustering, k = 5),
xlab = "latent dim 1",
ylab = "latent dim 2",
main = "Multidimensional scaling of trace variants")

save(dist_matrix, mds_embedds, file = "variant_distance_matrix.RData")
fig1 <- ggplot(data.table(mds_embedds),
aes(x=V1, y=V2, colour = factor(cutree(hclustering, k = 5)))) +
geom_point() +
labs(x = "latent dim 1", y = "latent dim 2", colour = "group",
title = "Multidimensional scaling of trace variants")
fig1

process_discovery_data[, time_window_id_SYSCALL_pid := paste0(time_window_id, "_", SYSCALL_pid)]
trace_dt <- trace_dt[process_discovery_data[, .(attack = is_attack[1]),
by = time_window_id_SYSCALL_pid],
on = "time_window_id_SYSCALL_pid"]
global_mean <- trace_dt[, mean(attack)]
variant_dt <- trace_dt[, .(attack_prob = (sum(attack) + global_mean)/(.N + 1),
n_of_traces = .N),
by = compacted_trace]
# here I'm relying on the assumption about the same ordering of variants - might be risky
variant_dt[, cluster_id := cutree(hclustering, k = 5)]
fig2 <- ggplot(data.table(mds_embedds), aes(x=V1, y=V2,
colour = variant_dt[, attack_prob],
#shape = factor(variant_dt[, cluster_id]),
size = variant_dt[, log(n_of_traces)]
)) +
geom_point() +
scale_color_gradient2(midpoint=0.5, low="blue", mid="white",
high="red", space ="Lab" ) +
labs(x = "latent dim 1", y = "latent dim 2", colour = "attack prob", size = "log(#traces)",
title = "Multidimensional scaling of trace variants")
fig2

dist_array <- as.matrix(dist_matrix)
variant_dt[, distance_to_cluster_members :=
{sapply(.I, function(index) {
id = variant_dt[index, cluster_id];
sum(dist_array[index, variant_dt[, which(cluster_id == id)]] *
variant_dt[cluster_id == id, n_of_traces])
})
}]
# selecting cluster representatives
n_of_reps <- 5
cluster_reps <- variant_dt[, {id = first(order(distance_to_cluster_members), n_of_reps);
.(index = .I[id],
n_of_traces = n_of_traces[id],
attack_prob = attack_prob[id],
distance_to_cluster_members = distance_to_cluster_members[id]
)},
by = cluster_id]
fig3 <- ggplot(data.table(mds_embedds), aes(x=V1, y=V2,
colour = variant_dt[, attack_prob],
#shape = factor(variant_dt[, cluster_id]),
size = variant_dt[, log(n_of_traces)]
)) +
geom_point() +
scale_color_gradient2(midpoint=0.5, low="blue", mid="white",
high="red", space ="Lab" ) +
geom_point(data = data.table(mds_embedds[cluster_reps[, index], ]),
color = variant_dt[cluster_reps[, index], factor(cluster_id)],
size = 2) +
labs(x = "latent dim 1", y = "latent dim 2", colour = "attack prob", size = "log(#traces)",
title = "Visualization of variants with marked cluster representatives")
fig3

# selecting the largest variants
n_of_largest <- 5
largest_variants <- variant_dt[, {id = last(order(n_of_traces), n_of_largest);
.(index = .I[id],
n_of_traces = n_of_traces[id],
attack_prob = attack_prob[id],
distance_to_cluster_members = distance_to_cluster_members[id]
)},
by = cluster_id]
fig4 <- ggplot(data.table(mds_embedds), aes(x=V1, y=V2,
colour = variant_dt[, attack_prob],
size = variant_dt[, log(n_of_traces)]
)) +
geom_point() +
scale_color_gradient2(midpoint=0.5, low="blue", mid="white",
high="red", space ="Lab" ) +
geom_point(data = data.table(mds_embedds[largest_variants[, index], ]),
color = variant_dt[largest_variants[, index], factor(cluster_id)],
size = 2) +
labs(x = "latent dim 1", y = "latent dim 2", colour = "attack prob", size = "log(#traces)",
title = "Visualization of variants with marked largest variants for each cluster")
fig4

# selecting the largest variants
n_of_extreme_attack_probs <- 5
min_trace_count <- 10
extreme_variants <- variant_dt[n_of_traces > min_trace_count,
{id = order(attack_prob);
id = c(first(id, n_of_extreme_attack_probs),
last(id, n_of_extreme_attack_probs));
.(index = .I[id],
n_of_traces = n_of_traces[id],
attack_prob = attack_prob[id],
distance_to_cluster_members = distance_to_cluster_members[id]
)},
by = .(cluster_id)]
fig5 <- ggplot(data.table(mds_embedds), aes(x=V1, y=V2,
colour = variant_dt[, attack_prob],
size = variant_dt[, log(n_of_traces)]
)) +
geom_point() +
scale_color_gradient2(midpoint=0.5, low="blue", mid="white",
high="red", space ="Lab" ) +
geom_point(data = data.table(mds_embedds[extreme_variants[, index], ]),
colour = variant_dt[extreme_variants[, index], factor(cluster_id)],
size = 2) +
labs(x = "latent dim 1", y = "latent dim 2", colour = "attack prob", size = "log(#traces)",
title = "Visualization of variants with extreme attack probabilities")
fig5

unique(rbind(cluster_reps, largest_variants, extreme_variants))
save(variant_dt, cluster_reps, largest_variants, extreme_variants,
file = "selected_variants.RData")
LS0tDQp0aXRsZTogIkN5YmVyc2VjdXJpdHkgVGhyZWF0IERldGVjdGlvbiBpbiB0aGUgQmVoYXZpb3Igb2YgSW9UIERldmljZXMiDQphdXRob3I6ICJIaWRkZW4gZm9yIHRoZSB0aW1lIG9mIHRoZSBkb3VibGUtYmxpbmQgcmV2aWV3IHByb2Nlc3MiDQpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgZGZfcHJpbnQ6IHBhZ2VkDQogICAgZmlnX2hlaWdodDogMTANCiAgICBmaWdfd2lkdGg6IDEwDQogICAgcm93cy5wcmludDogMTANCiAgICBjc3M6IGRvYy5jc3MNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBkZl9wcmludDogcGFnZWQNCiAgICBmaWdfaGVpZ2h0OiAxMA0KICAgIGZpZ193aWR0aDogMTANCiAgICByb3dzLnByaW50OiAxMA0KICAgIGNzczogZG9jLmNzcw0KLS0tDQoNCiMjIyBUaGUgcGxhbjoNCg0KMS4gTG9hZGluZyBhbGwgZGF0YS4NCjIuIEV4dHJhY3Rpbmcgc3RhdGlzdGljcyByZWdhcmRpbmcgUElEcyAoY2FzZXMpIGFuZCBzeXNjYWxsIHR5cGVzIChhY3Rpb25zKS4NCjMuIENvbXB1dGF0aW9uIG9mIGJhc2ljIGFnZ3JlZ2F0aW9ucyBhbmQgdmlzdWFsaXphdGlvbiBvZiB0aGUgcmVzdWx0cy4gIA0KDQpUaGUgb3JpZ2luYWwgc291cmNlIHN5c3RlbSBhdWRpdCBsb2dzIGFyZSB0aGUgcHJvcGVydHkgb2YgS25vd2xlZGdlUGl0LmFpIHBsYXRmb3JtIGFuZCBjYW4gYmUgZG93bmxvYWRlZCBmcm9tOg0KaHR0cHM6Ly9rbm93bGVkZ2VwaXQuYWkvZmVkY3Npcy0yMDIzLWNoYWxsZW5nZS8NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0UsIHJlc3VsdHM9RkFMU0V9DQpvcHRpb25zKHdpZHRoID0gMTIwKQ0KDQojIGluc3RhbGxhdGlvbiBvZiB0aGUgcmVxdWlyZWQgcGFja2FnZXMgKHVuY29tbWVudCB0aGUgbGluZXMgYmVsb3cpOg0KIyBpbnN0YWxsLnBhY2thZ2VzKGMoImRhdGEudGFibGUiLCAiYXJ1bGVzIiwgImFydWxlc1NlcXVlbmNlcyIsICJhcnVsZXNWaXoiLCANCiMgICAgICAgICAgICAgICAgICAgICJnZ3Bsb3QyIiwgImx1YnJpZGF0ZSIsICJmYXN0dGltZSIsICJNYXRyaXgiKSkNCg0KbGlicmFyeShkYXRhLnRhYmxlKQ0KbGlicmFyeShNYXRyaXgpDQpsaWJyYXJ5KGFydWxlcykNCmxpYnJhcnkoYXJ1bGVzU2VxdWVuY2VzKQ0KbGlicmFyeShhcnVsZXNWaXopDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkoZmFzdHRpbWUpDQoNCmxpYnJhcnkoYnVwYXZlcnNlKQ0KDQpuX2NvcmVzIDwtIDYNCg0KIyBsb2NhbCBkYXRhIHBhdGhzDQpkYXRhX2RpciA8LSAiZGF0YSINCmRhdGFfZGlyX3RyIDwtICJkYXRhL3RyYWluX2RhdGEiDQpkYXRhX2Rpcl90ZSA8LSAiZGF0YS90ZXN0X2RhdGEiDQoNCiMgbG9jYWwgZmlsZSBuYW1lcw0KdHJhaW5pbmdfYXR0YWNrc19maWxlIDwtICJ0cmFpbl9maWxlc19jb250YWluaW5nX2F0dGFja3MudHh0Ig0KdGVzdF9hdHRhY2tzX2ZpbGUgPC0gInRlc3RfZmlsZXNfY29udGFpbmluZ19hdHRhY2tzLnR4dCINCg0KIyBnZXR0aW5nIGxpc3RzIG9mIGZpbGVzIHdpdGggYXVkaXQgbG9ncw0KdHJhaW5pbmdfZmlsZV9saXN0IDwtIGRpcihmaWxlLnBhdGgoZ2V0d2QoKSwgZGF0YV9kaXJfdHIpKQ0KdGVzdF9maWxlX2xpc3QgPC0gZGlyKGZpbGUucGF0aChnZXR3ZCgpLCBkYXRhX2Rpcl90ZSkpDQphdHRhY2tfZmlsZV9uYW1lcyA8LSBjKHJlYWRMaW5lcyhmaWxlLnBhdGgoZGF0YV9kaXIsIHRyYWluaW5nX2F0dGFja3NfZmlsZSkpLA0KICAgICAgICAgICAgICAgICAgICAgICByZWFkTGluZXMoZmlsZS5wYXRoKGRhdGFfZGlyLCB0ZXN0X2F0dGFja3NfZmlsZSkpKQ0KDQojIGN1c3RvbSBmdW5jdGlvbiBkZWZpbml0aW9ucw0KZXh0cmFjdF9iYXNpY19pbmZvIDwtIGZ1bmN0aW9uKGxvZ3MpIHsNCiAgbl9vZl9waWRzIDwtIGxvZ3NbLCB1bmlxdWVOKFNZU0NBTExfcGlkKV0NCiAgbl9vZl91bmlxdWVfYWN0aW9ucyA8LSBsb2dzWywgdW5pcXVlTihTWVNDQUxMX3N5c2NhbGwpXQ0KICBuX29mX2V2ZW50cyA8LSBucm93KGxvZ3MpDQogIGF2Z19hY3Rpb25zX3Blcl9waWQgPC0gbl9vZl9ldmVudHMvbl9vZl9waWRzDQogIGFjdGlvbnNfcGVyX3BpZF9xdWFudHMgPC0gbG9nc1ssIC5OLCBTWVNDQUxMX3BpZF1bLCBxdWFudGlsZShOLCBjKDAsIDAuMSwgMC4yNSwgMC41LCAwLjc1LCAwLjksIDEuMCkpXQ0KICANCiAgbGlzdChuX29mX3BpZHMgPSBuX29mX3BpZHMsDQogICAgICAgbl9vZl91bmlxdWVfYWN0aW9ucyA9IG5fb2ZfdW5pcXVlX2FjdGlvbnMsDQogICAgICAgbl9vZl9ldmVudHMgPSBuX29mX2V2ZW50cywNCiAgICAgICBhdmdfYWN0aW9uc19wZXJfcGlkID0gYXZnX2FjdGlvbnNfcGVyX3BpZCwNCiAgICAgICBhY3Rpb25zX3Blcl9waWRfcXVhbnRzID0gYWN0aW9uc19wZXJfcGlkX3F1YW50cw0KICApDQp9DQoNCmdldFNlcXVlbmNlSUQgPC0gZnVuY3Rpb24oaXRlbXNldElEcykgew0KICBzYXBwbHkoc3Ryc3BsaXQoaXRlbXNldElEcywgIl8iKSwgZnVuY3Rpb24oeCkgcGFzdGUwKHhbMV0sICJfIiwgeFsyXSkpDQp9DQoNCmdldFRpbWVzdGFtcCA8LSBmdW5jdGlvbihpdGVtc2V0SURzKSB7DQogIHNhcHBseShzdHJzcGxpdChpdGVtc2V0SURzLCAiXyIpLCBmdW5jdGlvbih4KSBhcy5pbnRlZ2VyKHhbM10pKQ0KfQ0KDQojIGFuIGF1eGlsaWFyeSBmdW5jdGlvbiBmb3IgY29tcGFjdGluZyB0cmFjZXMNCmNvbXBhY3RfdHJhY2VzIDwtIGZ1bmN0aW9uKHRyYWNlX2R0LCB1bmlxdWVfYWN0aW9uX2lkcywgc2hvd19wcm9ncmVzcyA9IFRSVUUpIHsNCiAgdHJhY2VfZHRbLCBjb21wYWN0ZWRfdHJhY2UgOj0gY29weSh0cmFjZSldDQogIA0KICB0cmFjZV9kdFssIGNvbXBhY3RlZF90cmFjZSA6PSBnc3ViKCJyb290X2Nsb3NlX3llcyxyb290X29wZW5hdF95ZXMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyb290X2Nsb3NlX29wZW5hdF95ZXMiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb21wYWN0ZWRfdHJhY2UpXQ0KICB0cmFjZV9kdFssIGNvbXBhY3RlZF90cmFjZSA6PSBnc3ViKCIoLHJvb3RfY2xvc2Vfb3BlbmF0X3llcyl7Mix9IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIixtdWx0aV9yb290X2Nsb3NlX29wZW5hdF95ZXMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbXBhY3RlZF90cmFjZSldDQogIHRyYWNlX2R0WywgY29tcGFjdGVkX3RyYWNlIDo9IGdzdWIoIl5yb290X2Nsb3NlX29wZW5hdF95ZXMsbXVsdGlfcm9vdF9jbG9zZV9vcGVuYXRfeWVzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibXVsdGlfcm9vdF9jbG9zZV9vcGVuYXRfeWVzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb21wYWN0ZWRfdHJhY2UpXQ0KICB0cmFjZV9kdFssIGNvbXBhY3RlZF90cmFjZSA6PSBnc3ViKCJecm9vdF9jbG9zZV9vcGVuYXRfeWVzLHJvb3RfY2xvc2Vfb3BlbmF0X3llcyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm11bHRpX3Jvb3RfY2xvc2Vfb3BlbmF0X3llcyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29tcGFjdGVkX3RyYWNlKV0NCiAgDQogIGZvcihhY3Rpb24gaW4gdW5pcXVlX2FjdGlvbl9pZHMpIHsNCiAgICB0cmFjZV9kdFssIGNvbXBhY3RlZF90cmFjZSA6PSBnc3ViKHBhc3RlMCgiKCwiLCBhY3Rpb24sICIpezIsfSIpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgiLG11bHRpXyIsIGFjdGlvbiksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29tcGFjdGVkX3RyYWNlKV0NCiAgICB0cmFjZV9kdFssIGNvbXBhY3RlZF90cmFjZSA6PSBnc3ViKHBhc3RlMCgiXiIsIGFjdGlvbiwgIixtdWx0aV8iLCBhY3Rpb24pLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgibXVsdGlfIiwgYWN0aW9uKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb21wYWN0ZWRfdHJhY2UpXQ0KICAgIHRyYWNlX2R0WywgY29tcGFjdGVkX3RyYWNlIDo9IGdzdWIocGFzdGUwKCJeIiwgYWN0aW9uLCAiLCIsIGFjdGlvbiksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJtdWx0aV8iLCBhY3Rpb24pLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbXBhY3RlZF90cmFjZSldDQogICAgaWYoc2hvd19wcm9ncmVzcykgcHJpbnQoYWN0aW9uKQ0KICB9DQogIHRyYWNlX2R0WywgdW5pcXVlTihjb21wYWN0ZWRfdHJhY2UpXQ0KICB0cmFjZV9kdFssIGNvbXBhY3RlZF90cmFjZV9sZW5ndGggOj0gc2FwcGx5KHN0cnNwbGl0KGNvbXBhY3RlZF90cmFjZSwgIiwiKSwgbGVuZ3RoKV0NCiAgdHJhY2VfZHQNCn0NCmBgYA0KDQpUaGUgYXZhaWxhYmxlIGRhdGEgY29udGFpbnMgYHIgbGVuZ3RoKHRyYWluaW5nX2ZpbGVfbGlzdCkgKyBsZW5ndGgodGVzdF9maWxlX2xpc3QpYCBsb2cgZmlsZXMuIEVhY2ggb2YgdGhlIGZpbGVzIGNvcnJlc3BvbmRzIHRvIDEwIG1pbnV0ZXMgb2Ygc3lzY2FsbCBoaXN0b3J5LiBJbiB0b3RhbCwgYHIgbGVuZ3RoKGF0dGFja19maWxlX25hbWVzKWAgbG9nIGZpbGVzIChgciByb3VuZCgxMDAqbGVuZ3RoKGF0dGFja19maWxlX25hbWVzKS8obGVuZ3RoKHRyYWluaW5nX2ZpbGVfbGlzdCkgKyBsZW5ndGgodGVzdF9maWxlX2xpc3QpKSwgMilgXCUpIGNvcnJlc3BvbmQgdG8gYXR0YWNrcyBvbiB0aGUgZGV2aWNlcy4NCg0KVG8gZW5hYmxlIHRoZSBhbmFseXNpcyBvZiB0aGlzIGRhdGEgdXNpbmcgcHJvY2VzcyBtaW5pbmcgdGVjaG5pcXVlcywgSSB3b3VsZCBzdWdnZXN0IHRvIGNvbnNpZGVyIHRoZSBjb21iaW5hdGlvbiBvZiBhIHRpbWUgd2luZG93IElEICggX190aW1lX3dpbmRvd19pZF9fICkgYW5kIF9fU1lTQ0FMTF9waWRfXyB2YWx1ZXMgYXMgX2Nhc2VzXyBhbmQgY29tYmluYXRpb25zIG9mIF9fU1lTQ0FMTF9zeXNjYWxsX18sIF9fU1lTQ0FMTF9zdWNjZXNzX18sIGFuZCBtYXliZSBfX1BST0NFU1NfdWlkX18gdmFsdWVzIGFzIF9hY3Rpb25zXy4gDQoNCkkgdHJhbnNmb3JtIHRoZSBkYXRhIGJ5IHNlbGVjdGluZyBfX1NZU0NBTExfdGltZXN0YW1wX18sIF9fU1lTQ0FMTF9waWRfXywgX19TWVNDQUxMX3N1Y2Nlc3NfXywgX19QUk9DRVNTX3VpZF9fLCBhbmQgX19TWVNDQUxMX3N5c2NhbGxfXyBjb2x1bW5zLiBJIGFsc28gbWVyZ2UgYWxsIGRhdGEgdGFibGVzIGludG8gYSBzaW5nbGUgdGFibGUgZm9yIG1vcmUgY29udmVuaWVudCBwcm9jZXNzaW5nLiBGaW5hbGx5LCBJIGRpdmlkZSB0aGUgZGF0YSBpbnRvIHRocmVlIHBhcnRzOg0KICANCiAgLSBfcHJvY2Vzc19kaXNjb3ZlcnlfIHBhcnQsICAgDQogIC0gX21vZGVsX3RyYWluaW5nXyBwYXJ0LA0KICAtIF90ZXN0X2RhdGFfIHBhcnQuDQoNCmBgYHtyIHByb2Nlc3NlZF9kYXRhX2xvYWRpbmcsIHJlc3VsdHM9J2hpZGUnfQ0KbG9hZChmaWxlID0gInByb2Nlc3NfZGF0YV92Mi5SRGF0YSIpDQoNCnByb2Nlc3NfZGlzY292ZXJ5X2RhdGFbU1lTQ0FMTF9zdWNjZXNzID09ICIiLCBTWVNDQUxMX3N1Y2Nlc3MgOj0gIk5BIl0NCm1vZGVsX3RyYWluaW5nX2RhdGFbU1lTQ0FMTF9zdWNjZXNzID09ICIiLCBTWVNDQUxMX3N1Y2Nlc3MgOj0gIk5BIl0NCnRlc3RfZGF0YVtTWVNDQUxMX3N1Y2Nlc3MgPT0gIiIsIFNZU0NBTExfc3VjY2VzcyA6PSAiTkEiXQ0KDQpzZXRrZXkocHJvY2Vzc19kaXNjb3ZlcnlfZGF0YSwgdGltZV93aW5kb3dfaWQsIFNZU0NBTExfdGltZXN0YW1wKQ0Kc2V0a2V5KG1vZGVsX3RyYWluaW5nX2RhdGEsIHRpbWVfd2luZG93X2lkLCBTWVNDQUxMX3RpbWVzdGFtcCkNCnNldGtleSh0ZXN0X2RhdGEsIHRpbWVfd2luZG93X2lkLCBTWVNDQUxMX3RpbWVzdGFtcCkNCmBgYA0KDQpgYGB7ciBidXBhcl9leHBlcmltLCB3YXJuaW5nPUZBTFNFLCBmaWcuaG9sZD0naG9sZCcsIG91dC53aWR0aD0iMTAwJSIsIGZpZy53aWR0aD0xMn0NCmV2ZW50bG9nX2RhdGEgPC0gZXZlbnRsb2coYXMuZGF0YS5mcmFtZShwcm9jZXNzX2Rpc2NvdmVyeV9kYXRhKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgY2FzZV9pZCA9IGMoInRpbWVfd2luZG93X2lkIiwgIlNZU0NBTExfcGlkIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgIHRpbWVzdGFtcCA9ICJTWVNDQUxMX3RpbWVzdGFtcCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGFjdGl2aXR5X2lkID0gYygiUFJPQ0VTU191aWQiLCAiU1lTQ0FMTF9zeXNjYWxsIiwgIlNZU0NBTExfc3VjY2VzcyIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBhY3Rpdml0eV9pbnN0YW5jZV9pZCA9IGMoInRpbWVfd2luZG93X2lkIiwgIlNZU0NBTExfcGlkIiwgIlBST0NFU1NfdWlkIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU1lTQ0FMTF9zeXNjYWxsIiwgIlNZU0NBTExfc3VjY2VzcyIsICJTWVNDQUxMX3RpbWVzdGFtcCIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICByZXNvdXJjZV9pZCA9IGMoIlBST0NFU1NfdWlkIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgIGxpZmVjeWNsZV9pZCA9ICJpc19hdHRhY2siLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBvcmRlciA9ICJhbHBoYWJldGljYWwiDQogICAgICAgICAgICAgICAgICAgICAgICAgICkNCg0KZXZlbnRsb2dfZGF0YQ0KDQp0cmFjZV9kYXRhIDwtIHRyYWNlcyhldmVudGxvZ19kYXRhKQ0KdHJhY2VfZGF0YQ0KDQpmcmVxdWVudF90cmFjZXMgPC0gZmlsdGVyX3RyYWNlX2ZyZXF1ZW5jeShldmVudGxvZ19kYXRhLCBwZXJjZW50YWdlID0gLjgpDQpmcmVxdWVudF90cmFjZXMNCg0KYXR0YWNrX3RyYWNlcyA8LSBmaWx0ZXJfbGlmZWN5Y2xlX3ByZXNlbmNlKGV2ZW50bG9nX2RhdGEsIGxpZmVjeWNsZXMgPSAiVFJVRSIsIG1ldGhvZCA9ICJhbGwiKQ0KYXR0YWNrX3RyYWNlcw0KYGBgDQpgYGB7ciBwbG90aW5nX3ZhcmlhbnRzXzEsIHdhcm5pbmc9RkFMU0UsIGZpZy5ob2xkPSdob2xkJywgb3V0LndpZHRoPSIxMDAlIiwgZmlnLndpZHRoPTE4fQ0KZmxvd19wbG90IDwtIHByb2Nlc3NfbWFwKGZyZXF1ZW50X3RyYWNlcywgdHlwZSA9IGZyZXF1ZW5jeSgicmVsYXRpdmUiKSkNCmZsb3dfcGxvdA0KDQpmcmVxdWVudF9hdHRhY2tfdHJhY2VzIDwtIGZpbHRlcl90cmFjZV9mcmVxdWVuY3koYXR0YWNrX3RyYWNlcywgcGVyY2VudGFnZSA9IC44KQ0KYXR0YWNrc19mbG93X3Bsb3QgPC0gcHJvY2Vzc19tYXAoZnJlcXVlbnRfYXR0YWNrX3RyYWNlcywgdHlwZSA9IGZyZXF1ZW5jeSgicmVsYXRpdmUiKSkNCmF0dGFja3NfZmxvd19wbG90DQoNCnZhcmlhbnRfdml6IDwtIHRyYWNlX2V4cGxvcmVyKGV2ZW50bG9nX2RhdGEsIGNvdmVyYWdlID0gMC41KQ0KdmFyaWFudF92aXoNCmBgYA0KDQpgYGB7ciBjb21wYWN0aW5nX3RyYWNlc30NCmV2ZW50bG9nX2RhdGFfZHQgPC0gYXMuZGF0YS50YWJsZShldmVudGxvZ19kYXRhKQ0KDQp1bmlxdWVfYWN0aW9uX2lkcyA8LSBldmVudGxvZ19kYXRhX2R0WywgdW5pcXVlKFBST0NFU1NfdWlkX1NZU0NBTExfc3lzY2FsbF9TWVNDQUxMX3N1Y2Nlc3MpXQ0KdHJhY2VfZHQgPC0gZXZlbnRsb2dfZGF0YV9kdFssIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAuKHRyYWNlID0gcGFzdGUoUFJPQ0VTU191aWRfU1lTQ0FMTF9zeXNjYWxsX1NZU0NBTExfc3VjY2VzcywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICIsIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJhY2VfbGVuZ3RoID0gLk4pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IHRpbWVfd2luZG93X2lkX1NZU0NBTExfcGlkXQ0KDQpkaW0odHJhY2VfZHQpDQp0cmFjZV9kdFssIHVuaXF1ZU4odHJhY2UpXQ0KDQp0cmFjZV9kdCA8LSBjb21wYWN0X3RyYWNlcyh0cmFjZV9kdCwgdW5pcXVlX2FjdGlvbl9pZHMsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19wcm9ncmVzcyA9IEZBTFNFKQ0KYGBgDQoNCmBgYHtyIGNsdXN0ZXJpbmdfdHJhY2VzLCBmaWcuaG9sZD0naG9sZCcsIG91dC53aWR0aD0iMTAwJSIsIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTZ9DQpub3JtYWxpemVkX2xldmVuc2h0ZWluX2Rpc3QgPC0gZnVuY3Rpb24oeCwgeSkgew0KICBsZGlzdCA8LSBwcm94eTo6ZGlzdChsaXN0KHgpLCBsaXN0KHkpLCBtZXRob2QgPSAiTGV2ZW5zaHRlaW4iKQ0KICBhcy5udW1lcmljKGxkaXN0KS8obGVuZ3RoKHgpICsgbGVuZ3RoKHkpKQ0KfQ0KDQpkaXN0X21hdHJpeCA8LSBwcm94eTo6ZGlzdChzdHJzcGxpdCh0cmFjZV9kdFssIHVuaXF1ZShjb21wYWN0ZWRfdHJhY2UpXSwgIiwiKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSBub3JtYWxpemVkX2xldmVuc2h0ZWluX2Rpc3QpDQoNCmhjbHVzdGVyaW5nIDwtIGhjbHVzdChkaXN0X21hdHJpeCwgbWV0aG9kID0gIndhcmQuRCIpDQoNCm1kc19lbWJlZGRzIDwtIGNtZHNjYWxlKGRpc3RfbWF0cml4LCBrID0gMikNCnBsb3QobWRzX2VtYmVkZHMsIGNvbCA9IGN1dHJlZShoY2x1c3RlcmluZywgayA9IDUpLCANCiAgICAgeGxhYiA9ICJsYXRlbnQgZGltIDEiLCANCiAgICAgeWxhYiA9ICJsYXRlbnQgZGltIDIiLA0KICAgICBtYWluID0gIk11bHRpZGltZW5zaW9uYWwgc2NhbGluZyBvZiB0cmFjZSB2YXJpYW50cyIpDQpzYXZlKGRpc3RfbWF0cml4LCBtZHNfZW1iZWRkcywgZmlsZSA9ICJ2YXJpYW50X2Rpc3RhbmNlX21hdHJpeC5SRGF0YSIpDQoNCmZpZzEgPC0gZ2dwbG90KGRhdGEudGFibGUobWRzX2VtYmVkZHMpLCANCiAgICAgICAgICAgICAgIGFlcyh4PVYxLCB5PVYyLCBjb2xvdXIgPSBmYWN0b3IoY3V0cmVlKGhjbHVzdGVyaW5nLCBrID0gNSkpKSkgKw0KICBnZW9tX3BvaW50KCkgKyANCiAgbGFicyh4ID0gImxhdGVudCBkaW0gMSIsIHkgPSAibGF0ZW50IGRpbSAyIiwgY29sb3VyID0gImdyb3VwIiwgDQogICAgICAgdGl0bGUgPSAiTXVsdGlkaW1lbnNpb25hbCBzY2FsaW5nIG9mIHRyYWNlIHZhcmlhbnRzIikNCmZpZzENCmBgYA0KDQpgYGB7ciBhZGRpbmdfYXR0YWNrcywgZmlnLmhvbGQ9J2hvbGQnLCBvdXQud2lkdGg9IjEwMCUiLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD02fQ0KcHJvY2Vzc19kaXNjb3ZlcnlfZGF0YVssIHRpbWVfd2luZG93X2lkX1NZU0NBTExfcGlkIDo9IHBhc3RlMCh0aW1lX3dpbmRvd19pZCwgIl8iLCBTWVNDQUxMX3BpZCldDQp0cmFjZV9kdCA8LSB0cmFjZV9kdFtwcm9jZXNzX2Rpc2NvdmVyeV9kYXRhWywgLihhdHRhY2sgPSBpc19hdHRhY2tbMV0pLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSB0aW1lX3dpbmRvd19pZF9TWVNDQUxMX3BpZF0sIA0KICAgICAgICAgICAgICAgICAgICAgb24gPSAidGltZV93aW5kb3dfaWRfU1lTQ0FMTF9waWQiXQ0KDQpnbG9iYWxfbWVhbiA8LSB0cmFjZV9kdFssIG1lYW4oYXR0YWNrKV0NCnZhcmlhbnRfZHQgPC0gdHJhY2VfZHRbLCAuKGF0dGFja19wcm9iID0gKHN1bShhdHRhY2spICsgZ2xvYmFsX21lYW4pLyguTiArIDEpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgbl9vZl90cmFjZXMgPSAuTiksIA0KICAgICAgICAgICAgICAgICAgICAgICBieSA9IGNvbXBhY3RlZF90cmFjZV0NCg0KIyBoZXJlIEknbSByZWx5aW5nIG9uIHRoZSBhc3N1bXB0aW9uIGFib3V0IHRoZSBzYW1lIG9yZGVyaW5nIG9mIHZhcmlhbnRzIC0gbWlnaHQgYmUgcmlza3kNCnZhcmlhbnRfZHRbLCBjbHVzdGVyX2lkIDo9IGN1dHJlZShoY2x1c3RlcmluZywgayA9IDUpXQ0KDQoNCmZpZzIgPC0gZ2dwbG90KGRhdGEudGFibGUobWRzX2VtYmVkZHMpLCBhZXMoeD1WMSwgeT1WMiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91ciA9IHZhcmlhbnRfZHRbLCBhdHRhY2tfcHJvYl0sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjc2hhcGUgPSBmYWN0b3IodmFyaWFudF9kdFssIGNsdXN0ZXJfaWRdKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IHZhcmlhbnRfZHRbLCBsb2cobl9vZl90cmFjZXMpXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApKSArDQogIGdlb21fcG9pbnQoKSArIA0KICBzY2FsZV9jb2xvcl9ncmFkaWVudDIobWlkcG9pbnQ9MC41LCBsb3c9ImJsdWUiLCBtaWQ9IndoaXRlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgIGhpZ2g9InJlZCIsIHNwYWNlID0iTGFiIiApICsNCiAgbGFicyh4ID0gImxhdGVudCBkaW0gMSIsIHkgPSAibGF0ZW50IGRpbSAyIiwgY29sb3VyID0gImF0dGFjayBwcm9iIiwgc2l6ZSA9ICJsb2coI3RyYWNlcykiLA0KICAgICAgIHRpdGxlID0gIk11bHRpZGltZW5zaW9uYWwgc2NhbGluZyBvZiB0cmFjZSB2YXJpYW50cyIpDQpmaWcyDQpgYGANCg0KDQpgYGB7ciBzZWxlY3RpbmdfY2x1c3Rlcl9yZXBzLCBmaWcuaG9sZD0naG9sZCcsIG91dC53aWR0aD0iMTAwJSIsIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTZ9DQpkaXN0X2FycmF5IDwtIGFzLm1hdHJpeChkaXN0X21hdHJpeCkNCg0KdmFyaWFudF9kdFssIGRpc3RhbmNlX3RvX2NsdXN0ZXJfbWVtYmVycyA6PSANCiAgICAgICAgICAgICAge3NhcHBseSguSSwgZnVuY3Rpb24oaW5kZXgpIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZCA9IHZhcmlhbnRfZHRbaW5kZXgsIGNsdXN0ZXJfaWRdOyANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdW0oZGlzdF9hcnJheVtpbmRleCwgdmFyaWFudF9kdFssIHdoaWNoKGNsdXN0ZXJfaWQgPT0gaWQpXV0gKiANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFudF9kdFtjbHVzdGVyX2lkID09IGlkLCBuX29mX3RyYWNlc10pDQogICAgICAgICAgICAgICAgICAgICAgICAgIH0pDQogICAgICAgICAgICAgIH1dDQoNCiMgc2VsZWN0aW5nIGNsdXN0ZXIgcmVwcmVzZW50YXRpdmVzDQpuX29mX3JlcHMgPC0gNQ0KY2x1c3Rlcl9yZXBzIDwtIHZhcmlhbnRfZHRbLCB7aWQgPSBmaXJzdChvcmRlcihkaXN0YW5jZV90b19jbHVzdGVyX21lbWJlcnMpLCBuX29mX3JlcHMpOyANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC4oaW5kZXggPSAuSVtpZF0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5fb2ZfdHJhY2VzID0gbl9vZl90cmFjZXNbaWRdLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXR0YWNrX3Byb2IgPSBhdHRhY2tfcHJvYltpZF0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpc3RhbmNlX3RvX2NsdXN0ZXJfbWVtYmVycyA9IGRpc3RhbmNlX3RvX2NsdXN0ZXJfbWVtYmVyc1tpZF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICl9LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSBjbHVzdGVyX2lkXQ0KDQpmaWczIDwtIGdncGxvdChkYXRhLnRhYmxlKG1kc19lbWJlZGRzKSwgYWVzKHg9VjEsIHk9VjIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvdXIgPSB2YXJpYW50X2R0WywgYXR0YWNrX3Byb2JdLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI3NoYXBlID0gZmFjdG9yKHZhcmlhbnRfZHRbLCBjbHVzdGVyX2lkXSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSB2YXJpYW50X2R0WywgbG9nKG5fb2ZfdHJhY2VzKV0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSkgKw0KICBnZW9tX3BvaW50KCkgKyANCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQyKG1pZHBvaW50PTAuNSwgbG93PSJibHVlIiwgbWlkPSJ3aGl0ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICBoaWdoPSJyZWQiLCBzcGFjZSA9IkxhYiIgKSArDQogIGdlb21fcG9pbnQoZGF0YSA9IGRhdGEudGFibGUobWRzX2VtYmVkZHNbY2x1c3Rlcl9yZXBzWywgaW5kZXhdLCBdKSwNCiAgICAgICAgICAgICBjb2xvciA9IHZhcmlhbnRfZHRbY2x1c3Rlcl9yZXBzWywgaW5kZXhdLCBmYWN0b3IoY2x1c3Rlcl9pZCldLCANCiAgICAgICAgICAgICBzaXplID0gMikgKyANCiAgbGFicyh4ID0gImxhdGVudCBkaW0gMSIsIHkgPSAibGF0ZW50IGRpbSAyIiwgY29sb3VyID0gImF0dGFjayBwcm9iIiwgc2l6ZSA9ICJsb2coI3RyYWNlcykiLA0KICAgICAgIHRpdGxlID0gIlZpc3VhbGl6YXRpb24gb2YgdmFyaWFudHMgd2l0aCBtYXJrZWQgY2x1c3RlciByZXByZXNlbnRhdGl2ZXMiKQ0KZmlnMw0KDQojIHNlbGVjdGluZyB0aGUgbGFyZ2VzdCB2YXJpYW50cw0Kbl9vZl9sYXJnZXN0IDwtIDUNCmxhcmdlc3RfdmFyaWFudHMgPC0gdmFyaWFudF9kdFssIHtpZCA9IGxhc3Qob3JkZXIobl9vZl90cmFjZXMpLCBuX29mX2xhcmdlc3QpOyANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLihpbmRleCA9IC5JW2lkXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuX29mX3RyYWNlcyA9IG5fb2ZfdHJhY2VzW2lkXSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXR0YWNrX3Byb2IgPSBhdHRhY2tfcHJvYltpZF0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlzdGFuY2VfdG9fY2x1c3Rlcl9tZW1iZXJzID0gZGlzdGFuY2VfdG9fY2x1c3Rlcl9tZW1iZXJzW2lkXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApfSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gY2x1c3Rlcl9pZF0NCg0KZmlnNCA8LSBnZ3Bsb3QoZGF0YS50YWJsZShtZHNfZW1iZWRkcyksIGFlcyh4PVYxLCB5PVYyLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3VyID0gdmFyaWFudF9kdFssIGF0dGFja19wcm9iXSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSB2YXJpYW50X2R0WywgbG9nKG5fb2ZfdHJhY2VzKV0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSkgKw0KICBnZW9tX3BvaW50KCkgKyANCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQyKG1pZHBvaW50PTAuNSwgbG93PSJibHVlIiwgbWlkPSJ3aGl0ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICBoaWdoPSJyZWQiLCBzcGFjZSA9IkxhYiIgKSArDQogIGdlb21fcG9pbnQoZGF0YSA9IGRhdGEudGFibGUobWRzX2VtYmVkZHNbbGFyZ2VzdF92YXJpYW50c1ssIGluZGV4XSwgXSksDQogICAgICAgICAgICAgY29sb3IgPSB2YXJpYW50X2R0W2xhcmdlc3RfdmFyaWFudHNbLCBpbmRleF0sIGZhY3RvcihjbHVzdGVyX2lkKV0sIA0KICAgICAgICAgICAgIHNpemUgPSAyKSArIA0KICBsYWJzKHggPSAibGF0ZW50IGRpbSAxIiwgeSA9ICJsYXRlbnQgZGltIDIiLCBjb2xvdXIgPSAiYXR0YWNrIHByb2IiLCBzaXplID0gImxvZygjdHJhY2VzKSIsDQogICAgICAgdGl0bGUgPSAiVmlzdWFsaXphdGlvbiBvZiB2YXJpYW50cyB3aXRoIG1hcmtlZCBsYXJnZXN0IHZhcmlhbnRzIGZvciBlYWNoIGNsdXN0ZXIiKQ0KZmlnNA0KDQojIHNlbGVjdGluZyB0aGUgbGFyZ2VzdCB2YXJpYW50cw0Kbl9vZl9leHRyZW1lX2F0dGFja19wcm9icyA8LSA1DQptaW5fdHJhY2VfY291bnQgPC0gMTANCmV4dHJlbWVfdmFyaWFudHMgPC0gdmFyaWFudF9kdFtuX29mX3RyYWNlcyA+IG1pbl90cmFjZV9jb3VudCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAge2lkID0gb3JkZXIoYXR0YWNrX3Byb2IpOyANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWQgPSBjKGZpcnN0KGlkLCBuX29mX2V4dHJlbWVfYXR0YWNrX3Byb2JzKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXN0KGlkLCBuX29mX2V4dHJlbWVfYXR0YWNrX3Byb2JzKSk7IA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLihpbmRleCA9IC5JW2lkXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbl9vZl90cmFjZXMgPSBuX29mX3RyYWNlc1tpZF0sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhdHRhY2tfcHJvYiA9IGF0dGFja19wcm9iW2lkXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlzdGFuY2VfdG9fY2x1c3Rlcl9tZW1iZXJzID0gZGlzdGFuY2VfdG9fY2x1c3Rlcl9tZW1iZXJzW2lkXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKX0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IC4oY2x1c3Rlcl9pZCldDQoNCmZpZzUgPC0gZ2dwbG90KGRhdGEudGFibGUobWRzX2VtYmVkZHMpLCBhZXMoeD1WMSwgeT1WMiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91ciA9IHZhcmlhbnRfZHRbLCBhdHRhY2tfcHJvYl0sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gdmFyaWFudF9kdFssIGxvZyhuX29mX3RyYWNlcyldDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkpICsNCiAgZ2VvbV9wb2ludCgpICsgDQogIHNjYWxlX2NvbG9yX2dyYWRpZW50MihtaWRwb2ludD0wLjUsIGxvdz0iYmx1ZSIsIG1pZD0id2hpdGUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgaGlnaD0icmVkIiwgc3BhY2UgPSJMYWIiICkgKw0KICBnZW9tX3BvaW50KGRhdGEgPSBkYXRhLnRhYmxlKG1kc19lbWJlZGRzW2V4dHJlbWVfdmFyaWFudHNbLCBpbmRleF0sIF0pLA0KICAgICAgICAgICAgIGNvbG91ciA9IHZhcmlhbnRfZHRbZXh0cmVtZV92YXJpYW50c1ssIGluZGV4XSwgZmFjdG9yKGNsdXN0ZXJfaWQpXSwgDQogICAgICAgICAgICAgc2l6ZSA9IDIpICsgDQogIGxhYnMoeCA9ICJsYXRlbnQgZGltIDEiLCB5ID0gImxhdGVudCBkaW0gMiIsIGNvbG91ciA9ICJhdHRhY2sgcHJvYiIsIHNpemUgPSAibG9nKCN0cmFjZXMpIiwNCiAgICAgICB0aXRsZSA9ICJWaXN1YWxpemF0aW9uIG9mIHZhcmlhbnRzIHdpdGggZXh0cmVtZSBhdHRhY2sgcHJvYmFiaWxpdGllcyIpDQpmaWc1DQoNCnVuaXF1ZShyYmluZChjbHVzdGVyX3JlcHMsIGxhcmdlc3RfdmFyaWFudHMsIGV4dHJlbWVfdmFyaWFudHMpKQ0Kc2F2ZSh2YXJpYW50X2R0LCBjbHVzdGVyX3JlcHMsIGxhcmdlc3RfdmFyaWFudHMsIGV4dHJlbWVfdmFyaWFudHMsDQogICAgIGZpbGUgPSAic2VsZWN0ZWRfdmFyaWFudHMuUkRhdGEiKQ0KYGBgDQoNCg0KDQoNClwgIA0KXCAgDQoNCg0K